package au.edu.qut.bpm.genbinder.models;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.deckfour.xes.model.XAttributeMap;
import org.deckfour.xes.model.buffered.XAttributeMapBufferedImpl;

public class Case {
	private String id;

	private AbstractLog log;
	private long arrival;
	private Set<Activity> activities;
	private Map<Activity, Set<Activity>> partialOrder; // Precedence: 1 comes
														// before ALL 2

	private XAttributeMap attributes;

	public Case(String id, AbstractLog log, long start) {
		this(id, log, start, new XAttributeMapBufferedImpl());
	}

	public Case(String id, AbstractLog log, long arrival, XAttributeMap attributes) {
		this.id = id;
		this.log = log;
		this.arrival = arrival;
		this.activities = new HashSet<Activity>();
		this.partialOrder = new HashMap<Activity, Set<Activity>>();
		this.attributes = attributes;
	}

	public String getId() {
		return id;
	}

	public AbstractLog getLog() {
		return log;
	}

	public Set<Activity> getActivities() {
		return activities;
	}
	
	public synchronized List<Activity> getOrderedTrace() {
		List<Activity> newOrder = new ArrayList<Activity>();
		Set<Activity> toPlace = new HashSet<Activity>(getActivities());
		int size = toPlace.size();
		while (size > 0) {
			List<Activity> left = new ArrayList<Activity>(toPlace);
			Collections.shuffle(left); // Randomize the solution
			for (Activity a : left) {
				boolean ok = true;
				for (Activity before : newOrder) {
					if (!isOrderAllowed(before, a))
						ok = false;
					if (!ok)
						break;
				}
				for (Activity after : left) {
					if (after.equals(a))
						continue;
					if (!isOrderAllowed(a, after))
						ok = false;
					if (!ok)
						break;
				}
				if (!ok)
					continue;
				newOrder.add(a);
				toPlace.remove(a);
				break;
			}
			size = toPlace.size();
		}
		return newOrder;
	}

	public XAttributeMap getAttributes() {
		return attributes;
	}

	public long getArrival() {
		return arrival;
	}

	public Map<Activity, Set<Activity>> getPartialOrder() {
		return partialOrder;
	}

	public boolean isOrderAllowed(Activity first, Activity second) {
		if (partialOrder.containsKey(second)) {
			if (partialOrder.get(second).contains(first))
				return false; // second BEFORE first
		} else if (partialOrder.containsKey(first)) {
			if (partialOrder.get(first).contains(second))
				return true;
		}
		return true;
	}

	@Override
	public String toString() {
		return id;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Case other = (Case) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

}
